<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>观察者模式</title>
  </head>

  <body>
    <button id="btnSell">开售</button>
    <button id="btnCheck">开奖</button>

    <!-- 观察者模式【父类/基类】封装 -->
    <script>
      /* 被观察者 Observable */
      class Observable {
        /* 创建对象时初始化一个观察者列表 */
        constructor(name) {
          this.name = name;
          this.observers = [];
        }

        /* 注册观察者 */
        registerObserver(ob) {
          this.observers.push(ob);

          // 您观察的对象正是在下
          ob.observable = this
        }

        /* 注销观察者 */
        unregisterObserver(ob) {
          this.observers = this.observers.filter((o) => o !== ob);
        }

        /* 发布事件 emit publish event */
        emit(event) {
          // 通知所有观察者响应事件
          this.observers.forEach((ob) => ob.onEvent(event));
        }
      }

      /* 观察者 */
      class Observer {
        /*  */
        constructor(name) {
          this.name = name;
        }

        /* 响应事件 */
        onEvent(event) {
          // [Object Person] {"type":"开奖"}
          console.log(`${this.name}响应事件${JSON.stringify(event)}`);
        }
      }
    </script>

    <!-- 观察者具体实现类封装 -->
    <script>
      /* 被观察者：每天都开奖的双色球 */
      class Lottery extends Observable {
        constructor(name) {
          // 调用父类构造器
          super(name);

          /* 周期性地开售/开奖的定时器 */
          this.timer = null;
        }

        /* 周期性地开售/开奖 */
        autoRun() {
          if (!this.timer) {

            this.timer = setInterval(() => {
              // 发布事件：上一期开奖
              const code = parseInt(Math.random() * 3);
              console.log("发布开奖事件", code);
              this.emit({
                type: "开奖",
                code, //[0,5]
              });

              // 发布事件：本期开售
              this.emit({
                type: "开售",
              });
            }, 3000);

          }
        }

        stop() {
          if(this.timer){
            clearInterval(this.timer);
            this.timer = null
          }
          console.log(this.name,"已停止");
        }
      }

      /* 观察者 */
      class Player extends Observer {
        /* 重写构造器 */
        constructor(name) {
          super(name);
          this.code = null;
        }

        /* 重写事件响应函数 */
        onEvent(event) {
          // super.onEvent(event)
          switch (event.type) {
            case "开售":
              this.buy();
              break;

            case "开奖":
              this.check(event);
              break;

            default:
              break;
          }
        }

        /* 购买彩票 */
        buy() {
          this.code = parseInt(Math.random() * 3);
          console.log(this.name, "购买了", this.code);
        }

        /* 开奖/兑奖 */
        check(event) {
          if (this.code === event.code) {
            console.log(`恭喜玩家${this.name}中奖！ta的女神觉得他很有内涵!`);

            // 让彩票机停售
            this.observable.stop()

          } else {
            console.log(`${this.name}灰头土脸地回家撸代码了`);
          }
          console.log("");
        }
      }
    </script>

    <!-- 业务逻辑 -->
    <script>
      // 创建彩票机
      const lot = new Lottery("双色球");

      // 创建玩家*2
      const fg = new Player("发哥");
      const tiger = new Player("今晚打老虎");

      // 玩家注册为观察者
      lot.registerObserver(fg);
      lot.registerObserver(tiger);
      lot.unregisterObserver(fg);
      console.log(lot.observers);

      /* 彩票机自动跑起 */
      lot.autoRun();

      // 点击按钮：彩票机发布事件
      btnSell.onclick = () => {
        lot.emit({ type: "开售" });
      };

      btnCheck.onclick = () => {
        lot.emit({ type: "开奖" });
      };
    </script>
    
  </body>
</html>
